/* -------------------------------------------------------------------------
 *
 * contrib/sepgsql/database.c
 *
 * 与数据库对象对应的例程
 *
 * Copyright (c) 2010-2022, PostgreSQL Global Development Group
 *
 * -------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/dependency.h"
#include "catalog/pg_database.h"
#include "commands/dbcommands.h"
#include "commands/seclabel.h"
#include "sepgsql.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/snapmgr.h"

/*  
 * sepgsql_database_post_create  
 *  
 * 此例程为新定义的数据库分配一个默认的安全标签，并检查其创建所需的权限。  
 */  
void sepgsql_database_post_create(Oid fc_databaseId, const char *fc_dtemplate)
{
	Relation	fc_rel;
	ScanKeyData fc_skey;
	SysScanDesc fc_sscan;
	HeapTuple	fc_tuple;
	char	   *fc_tcontext;
	char	   *fc_ncontext;
	ObjectAddress fc_object;
	Form_pg_database fc_datForm;
	StringInfoData fc_audit_name;

	/*  
	 * 源数据库的 Oid 并未保存在 pg_database 目录中，因此我们使用上下文信息收集其标识符。如果为 NULL，则其默认值为 "template1"，根据 createdb()。  
 */  
	if (!fc_dtemplate)
		fc_dtemplate = "template1";

	fc_object.classId = DatabaseRelationId;
	fc_object.objectId = get_database_oid(fc_dtemplate, false);
	fc_object.objectSubId = 0;

	fc_tcontext = sepgsql_get_label(fc_object.classId,
								 fc_object.objectId,
								 fc_object.objectSubId);

	/*  
	 * 检查 db_database:{getattr} 权限  
 */  
	initStringInfo(&fc_audit_name);
	appendStringInfoString(&fc_audit_name, quote_identifier(fc_dtemplate));
	sepgsql_avc_check_perms_label(fc_tcontext,
								  SEPG_CLASS_DB_DATABASE,
								  SEPG_DB_DATABASE__GETATTR,
								  fc_audit_name.data,
								  true);

	/*  
	 * 根据客户端和源数据库的安全标签对新创建的数据库计算默认的安全标签。  
	 *  
	 * XXX - 即将发布的 libselinux 版本支持使用对象名称处理默认安全标签的特殊处理。  
 */  
	fc_rel = table_open(DatabaseRelationId, AccessShareLock);

	ScanKeyInit(&fc_skey,
				Anum_pg_database_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_databaseId));

	fc_sscan = systable_beginscan(fc_rel, DatabaseOidIndexId, true,
							   SnapshotSelf, 1, &fc_skey);
	fc_tuple = systable_getnext(fc_sscan);
	if (!HeapTupleIsValid(fc_tuple))
		elog(ERROR, "could not find tuple for database %u", fc_databaseId);

	fc_datForm = (Form_pg_database) GETSTRUCT(fc_tuple);

	fc_ncontext = sepgsql_compute_create(sepgsql_get_client_label(),
									  fc_tcontext,
									  SEPG_CLASS_DB_DATABASE,
									  NameStr(fc_datForm->datname));

	/*  
	 * 检查 db_database:{create} 权限  
 */  
	resetStringInfo(&fc_audit_name);
	appendStringInfoString(&fc_audit_name,
						   quote_identifier(NameStr(fc_datForm->datname)));
	sepgsql_avc_check_perms_label(fc_ncontext,
								  SEPG_CLASS_DB_DATABASE,
								  SEPG_DB_DATABASE__CREATE,
								  fc_audit_name.data,
								  true);

	systable_endscan(fc_sscan);
	table_close(fc_rel, AccessShareLock);

	/*  
	 * 为新数据库分配默认安全标签  
 */  
	fc_object.classId = DatabaseRelationId;
	fc_object.objectId = fc_databaseId;
	fc_object.objectSubId = 0;

	SetSecurityLabel(&fc_object, SEPGSQL_LABEL_TAG, fc_ncontext);

	pfree(fc_ncontext);
	pfree(fc_tcontext);
}

/*  
 * sepgsql_database_drop  
 *  
 * 它检查删除提供的数据库所需的权限  
 */  
void sepgsql_database_drop(Oid fc_databaseId)
{
	ObjectAddress fc_object;
	char	   *fc_audit_name;

	/*  
	 * 检查 db_database:{drop} 权限  
 */  
	fc_object.classId = DatabaseRelationId;
	fc_object.objectId = fc_databaseId;
	fc_object.objectSubId = 0;
	fc_audit_name = getObjectIdentity(&fc_object, false);

	sepgsql_avc_check_perms(&fc_object,
							SEPG_CLASS_DB_DATABASE,
							SEPG_DB_DATABASE__DROP,
							fc_audit_name,
							true);
	pfree(fc_audit_name);
}

/*  
 * sepgsql_database_post_alter  
 *  
 * 它检查更改提供的数据库的权限  
 */  
void sepgsql_database_setattr(Oid fc_databaseId)
{
	ObjectAddress fc_object;
	char	   *fc_audit_name;

	/*  
	 * 检查 db_database:{setattr} 权限  
 */  
	fc_object.classId = DatabaseRelationId;
	fc_object.objectId = fc_databaseId;
	fc_object.objectSubId = 0;
	fc_audit_name = getObjectIdentity(&fc_object, false);

	sepgsql_avc_check_perms(&fc_object,
							SEPG_CLASS_DB_DATABASE,
							SEPG_DB_DATABASE__SETATTR,
							fc_audit_name,
							true);
	pfree(fc_audit_name);
}

/*  
 * sepgsql_database_relabel  
 *  
 * 它检查使用 `seclabel` 重新标记提供的数据库的权限  
 */  
void sepgsql_database_relabel(Oid fc_databaseId, const char *fc_seclabel)
{
	ObjectAddress fc_object;
	char	   *fc_audit_name;

	fc_object.classId = DatabaseRelationId;
	fc_object.objectId = fc_databaseId;
	fc_object.objectSubId = 0;
	fc_audit_name = getObjectIdentity(&fc_object, false);

	/*  
	 * 检查 db_database:{setattr relabelfrom} 权限  
 */  
	sepgsql_avc_check_perms(&fc_object,
							SEPG_CLASS_DB_DATABASE,
							SEPG_DB_DATABASE__SETATTR |
							SEPG_DB_DATABASE__RELABELFROM,
							fc_audit_name,
							true);

	/*  
	 * 检查 db_database:{relabelto} 权限  
 */  
	sepgsql_avc_check_perms_label(fc_seclabel,
								  SEPG_CLASS_DB_DATABASE,
								  SEPG_DB_DATABASE__RELABELTO,
								  fc_audit_name,
								  true);
	pfree(fc_audit_name);
}
